#pragma once

#include "rewine/IImport.hpp"

#include "wininc/pe.h"

#include <memory>
#include <string>
#include <vector>

namespace rewine
{

class IImage;

template<class Slot>
class CImport : public IImport<Slot> {
private:

    unsigned int        m_Index = -1;
    std::string         m_Name;
    Slot *              m_pSlot;

    ValueSynchronizer   m_Synchronizer;

protected:

    virtual Slot convertPointerToSlotValue(void *p) const = 0;
    virtual void * convertSlotValueToPointer(Slot s) const = 0;

private:

    bool setValueHandler(sync_value_t value) {
        if (getValueHandler() == value) {
            return false;
        }
        *m_pSlot = convertPointerToSlotValue(value.address);
        return true;
    }

    sync_value_t getValueHandler() {
        return sync_value_t(true, convertSlotValueToPointer(*m_pSlot));
    }

public:

    CImport() {
        m_Synchronizer.setValueHandler = std::bind(&CImport::setValueHandler, this, std::placeholders::_1);
        m_Synchronizer.getValueHandler = std::bind(&CImport::getValueHandler, this);
    }

    ~CImport() override = default;

public:

    void setIndex(unsigned int index) {
        m_Index = index;
    }

    void setName(const std::string &name) {
        m_Name = name;
    }

    void setSlotAddress(Slot *pSlot) {
        m_pSlot = pSlot;
    }

    unsigned int getIndex() const override {
        return m_Index;
    }

    std::string getName() const override {
        return m_Name;
    }

    Slot * getSlotAddress() const {
        return m_pSlot;
    }

public:

    void setInvalid() override {
        setSyncValue(sync_value_t());
    }

    bool isValid() override {
        return getSyncValue().valid;
    }

    void setValue(void *address) override {
        setSyncValue(sync_value_t(true, address));
    }

    void * getValue() override {
        return getSyncValue().address;
    }

public:

    void setSyncValue(sync_value_t value) {
        m_Synchronizer.setValue(value, true);
    }

    sync_value_t getSyncValue() {
        return m_Synchronizer.getValue();
    }

    ValueSynchronizer& getValueSynchronizerRef() override {
        return m_Synchronizer;
    }

    void bindTo(ValueSynchronizer& source) override {
        m_Synchronizer.follow(source);
    }

    void unbind() override {
        m_Synchronizer.unfollow();
    }

};

} // namespace rewine
